package TrafficSim;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.util.List;
import java.util.Vector;
import javax.swing.JLabel;
import javax.vecmath.Vector2d;

import TrafficSim.Intersection.TrafficDirection;

@SuppressWarnings("serial")
public class Simulation extends JLabel {
	private Graphics2D g2d;
	private boolean init = true;
	private ZoomAndPanListener zoomAndPanListener;
	private VehicleLists v;
	private Vector<Intersection> graph;

	public Simulation(VehicleLists vehicleLists,
			Vector<Intersection> intersections) {
		this.zoomAndPanListener = new ZoomAndPanListener();
		this.addMouseListener(zoomAndPanListener);
		this.addMouseMotionListener(zoomAndPanListener);
		this.addMouseWheelListener(zoomAndPanListener);
		this.v = vehicleLists;
		this.graph = intersections;
	}

	public void paint(Graphics g) {
		// super.paintComponents(g);
		this.g2d = (Graphics2D) g;
		List<Vehicle> vec = this.v.getVehicleList();
		if (init) {
			// Initialize the viewport
			init = false;
			zoomAndPanListener.setCoordTransform(((Graphics2D) g)
					.getTransform());
		} else {
			// Restore the viewport after it was updated by the
			// ZoomAndPanListener
			((Graphics2D) g).setTransform(zoomAndPanListener
					.getCoordTransform());
		}
		double scale = zoomAndPanListener.getCoordTransform().getScaleX();
		Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
		double actualX = zoomAndPanListener.getCoordTransform().getTranslateX()
				/ -scale;
		double actualY = zoomAndPanListener.getCoordTransform().getTranslateY()
				/ -scale;
		double actualWidth = dim.width / scale;
		double actualHeight = dim.height / scale;
		for (int i = 0; i < vec.size(); i++) {
			try {
				Vehicle veh = vec.get(i);
				if (veh.getID() == 1){
					g2d.setColor(Color.blue);
				}else{g2d.setColor(Color.black);}
				Vector2d pos = veh.getPosition();
				if (actualX <= pos.x && pos.x <= actualX + actualWidth && actualY <= pos.y && pos.y <= actualHeight + actualY) {
					try {
						Vector2d pos1 = calcPointNearIntersection(veh);
						Ellipse2D e = new Ellipse2D.Double(pos1.x - 2,
								pos1.y - 2, 4, 4);
						g2d.fill(e);
						g2d.draw(e);

					} catch (NullPointerException ex) {

						Ellipse2D e = new Ellipse2D.Double(pos.x - 2,
								pos.y - 2, 4, 4);
						g2d.fill(e);
						g2d.draw(e);

					}
				}
			} catch (ArrayIndexOutOfBoundsException ex) {
				break;
			}
		}

		for (Intersection i : this.graph) {
			for (Intersection j : i.getNextIntersections()) {
				if (j.getTLC() != null) {
					paintTrafficLight(g2d, i.getStreet(j));
				}
			}
		}
		this.setOpaque(true);
	}

	private Vector2d calcPointNearIntersection(Vehicle v) {
		double x = 0;
		Street currentStreet = v.getCurrentStreet();
		Street nextStreet = v.getNextStreet();
		Intersection nextIntersection = v.returnNextIntersection();
		Point pos = new Point((int) v.getPosition().x, (int) v.getPosition().y);

		Point p0 = null;
		Point p1 = null;
		Point p2 = null;

		if (pos.distance(nextIntersection.getPosition()) <= 20) {
			if (nextIntersection.getDirection(currentStreet, nextStreet) == TrafficDirection.STRAIGHT) {
				return null;
			}
			// System.out.println("driving in "+ nextStreet);
			p0 = getStartPoint(truncateStreet(currentStreet, 20),
					nextIntersection);
			p1 = getStartPoint(currentStreet, nextIntersection);
			p2 = getStartPoint(truncateStreet(nextStreet, 20), nextIntersection);
			x = (p0.distance(pos) / p0.distance(p1)) * 0.5;
			// System.out.println("1: "+x);
			Vector2d v0 = new Vector2d(p0.getX(), p0.getY());
			Vector2d v1 = new Vector2d(p1.getX(), p1.getY());
			Vector2d v2 = new Vector2d(p2.getX(), p2.getY());
			v0.scale(Math.pow((1 - x), 2));
			v1.scale(2 * x * (1 - x));
			v2.scale(Math.pow(x, 2));
			x = v0.x + v1.x + v2.x;
			double y = v0.y + v1.y + v2.y;
			Vector2d p = new Vector2d(x, y);
			return p;
		}

		Intersection currentIntersection = v.returnCurrentIntersection();
		Street lastStreet = v.getlastStreet();
		if (pos.distance(currentIntersection.getPosition()) <= 20) {
			if (currentIntersection.getDirection(lastStreet, currentStreet) == TrafficDirection.STRAIGHT) {
				return null;
			}
			// System.out.println("out of "+ currentStreet);
			p0 = getStartPoint(truncateStreet(lastStreet, 20),
					currentIntersection);
			p1 = getStartPoint(lastStreet, currentIntersection);
			p2 = getStartPoint(truncateStreet(currentStreet, 20),
					currentIntersection);
			x = ((p1.distance(pos) / p1.distance(p2)) * 0.5 + 0.5);
			// System.out.println("2: "+x);
		}
		Vector2d v0 = new Vector2d(p0.getX(), p0.getY());
		Vector2d v1 = new Vector2d(p1.getX(), p1.getY());
		Vector2d v2 = new Vector2d(p2.getX(), p2.getY());
		v0.scale(Math.pow((1 - x), 2));
		v1.scale(2 * x * (1 - x));
		v2.scale(Math.pow(x, 2));
		x = v0.x + v1.x + v2.x;
		double y = v0.y + v1.y + v2.y;
		Vector2d p = new Vector2d(x, y);
		return p;
	}

	private Point getStartPoint(Street s, Intersection i) {
		Point tempStart = new Point(s.getStart().x, s.getStart().y);
		Point tempEnd = new Point(s.getEnd().x, s.getEnd().y);
		if (tempEnd.distance(i.getPosition()) < tempStart.distance(i
				.getPosition())) {
			tempStart = tempEnd;

		}
		return tempStart;
	}

	public ZoomAndPanListener getListener() {
		return this.zoomAndPanListener;
	}

	public Street truncateStreet(Street s, int cut) {
		Vector2d dir = s.getDirection();
		dir.normalize();
		dir.scale(cut);
		Street temp = new Street(1);
		Vector2d start = new Vector2d(s.getStart().x, s.getStart().y);
		Vector2d end = new Vector2d(s.getEnd().x, s.getEnd().y);
		start.add(dir);
		end.sub(dir);
		temp.initStreet(new Point((int) start.x, (int) start.y), new Point(
				(int) end.x, (int) end.y));
		return temp;
	}

	private void paintTrafficLight(Graphics2D g, Street s) {
		g.setStroke(new BasicStroke(3f));
		Point2D loc = s.getTrafficLightPosition();
		TrafficLight tl = s.getTrafficLight();
		Ellipse2D tle = new Ellipse2D.Double(loc.getX() - 2, loc.getY() - 2, 4,
				4);
		if (tl != null) {
			switch (tl.getTlState()) {
			case GREEN:
				g.setColor(Color.green);
				break;
			case YELLOW:
				g.setColor(Color.yellow);
				break;
			case RED:
				g.setColor(Color.red);
				break;
			case BLINK:
				g.setColor(Color.pink);
				break;
			case REDYELLOW:
				g.setColor(Color.orange);
				break;
			}
			g.fill(tle);
			g.draw(tle);
		}
	}
}